#include <iostream>
#include <cstring>
using namespace std;

const int N = 20;           //图最大的维度
const int M = 1 << N;       //状态最大的维度
const int INF = 0x3f3f3f3f; //表示极大值，虽然这里没有用

int n;       //获取维度
int a[N][N]; //获取邻接矩阵
int f[M][N]; //状态表示

int main()
{
    freopen("cin.txt", "r", stdin);
    cin >> n;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            cin >> a[i][j];
    memset(f, 0x3f, sizeof f);                                                //先全部初始化为极大值来防止0更新不动
    f[1][0] = 0;                                                              //从0到0只经过0不需要走路
    for (int i = 2; i < 1 << n; ++i)                                          //其实这里的状态应该从0开始，但是从0开始没意义啊，连本来要到的点都到不了,1也没意义，除了自己到自己，哪儿都到不了，而且前面已经更新了自己到自己
        for (int j = 1; j < n; ++j)                                           //这里的点本来也该从0开始，但是本来起点是0，要不重不漏的到0，那就不能过别的点，所以没必要更新，这里直接1开始
            if (i >> j & 1)                                                   //还得判断当前压缩状态是不是可达j啊，不然这个状态和这个点他不搭啊
                for (int k = 0; k < n; ++k)                                   //这里得从0开始了，毕竟上一个点是0也不稀奇
                    if (i >> k & 1)                                           //一样得判断该状态过k，不然这个状态表示都是没有意义的
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + a[k][j]); //如果都满足，那就是集合中的一员，更新就完了
    cout << f[(1 << n) - 1][n - 1];                                           //所有点都得过，还得到n-1
    return 0;
}